#include <iostream>
#include <cassert>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "Task.hpp"

using namespace std;

const int num = 5;
static int number = 1;

class channel
{
public:
    channel(int fd, pid_t id) : ctrlfd(fd), workerid(id)
    {
        name = "channel-" + std::to_string(number++);
    }

    int ctrlfd;
    pid_t workerid;
    std::string name;
};
// 传参形式：(建议)
// 1.作为输入参数时，const &
// 2.作为输出参数时，*
// 3.输入输出参数时，&

void Work()
{
    while (true)
    {
        int code = 0;
        ssize_t n = read(0, &code, sizeof(code));
        if(n == sizeof(code))
        {
            if (!init.CheckSafe(code))
            {
                continue;
            }
        init.RunTask(code);
        }
        else if(n == 0)
        {
            break;
        }else{}
        (void)n; // 明确地告诉编译器变量 n 的值被有意忽略
        
    }
}

void CreateChannels(std::vector<channel> *c)
{
    std::vector<int> temp;
    // 1.定义并创建管道
    for (int i = 0; i < num; ++i)
    {
        int pipefd[2];
        int n = pipe(pipefd);
        assert(n == 0);
        (void)n;

        // 2.创建进程
        pid_t id = fork();
        assert(id != -1);
        if (id == 0) // child
        {
            if(!temp.empty())
            {
                for(auto fd : temp)
                {
                    close(fd);//关闭不该有的写端
                }
            }
            // 3.构建单向通信信道
            close(pipefd[1]);
            dup2(pipefd[0], 0);
            Work();
            exit(0);//会自动关闭自己打开的fd
        }
        // father
        close(pipefd[0]);
        c->push_back(channel(pipefd[1], id));
        temp.push_back(pipefd[1]);// 子进程在拷贝父进程的文件描述符表时也会把父进程的写端拷贝,
        //(比如子进程2会拷贝父进程指向管道1的写端，子进程2就多出来一个写端)(本来是父进程的一个写端对应子进程的一个读端)，
        //会导致管道不再是单向通信，因此要手动关闭
    }
}

void PrintDebug(const std::vector<channel> &c)
{
    for (const auto &channel : c)
    {
        std::cout << channel.name << ", " << channel.ctrlfd << ", " << channel.workerid << endl;
    }
}

void SendCommand(const std::vector<channel>& channels,bool flag, int num = -1)
{
    int pos = 0;
    while (1)
    {
        // 1.选择任务
        int command = init.SelectTask();
        // 2.选择信道（进程）
        //  较为平均地将任务交给进程，要考虑子进程完成任务的负载均衡问题
        const auto &c = channels[pos++];
        pos %= channels.size();
        //debug
        std::cout << "send command " << init.ToDesc(command) << "[" << command << "]" << " in " 
        << c.name << " worker is " << c.workerid << std::endl;

        // 3.发送任务
        write(c.ctrlfd, &command, sizeof(command));
        //4. 判断是否要退出
        if(!flag)
        {
            num--;
            if(num <= 0) break;
        }
        sleep(1);
    }
    std::cout << "SendCommand done..." << std::endl;
}

void ReleaseChannels(const std::vector<channel>& channels)
{
    for(const auto& c : channels)
    {
        close(c.ctrlfd);
    }
    for(const auto& c : channels)
    {
        pid_t rid = waitpid(c.workerid,nullptr,0);
        if(rid == c.workerid)
        {
            std::cout << "wait child: " << c.workerid << " success" << std::endl;
        }
    }
}

int main()
{
    std::vector<channel> channels;
    CreateChannels(&channels);  
    
    const bool g_always_loop = true;
    SendCommand(channels,!g_always_loop,100000);
    //SendCommand(channels,!g_always_loop);
    // PrintDebug(channels);
    // sleep(10);

    // 回收资源，想让子进程退出，并且释放管道，只需要关闭写端
   ReleaseChannels(channels);
    return 0;
}